﻿using System.Configuration;
using System.IO;
using Core;
using Core.Interfaces;
using Core.IocHelpers;
using Core.Settings;
using DataMain.Mappings;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Context;
using NHibernate.Tool.hbm2ddl;

namespace DataMain
{
  /// <summary>...need to vastly improve this!</summary>
  [AutoRegister]
  public class SessionProvider : ISessionProvider
  {
    private static FluentConfiguration _configuration;
    private static ISessionFactory _sessionFactory;

    // TODO: glittle 2009-04-12 - How can client app change the connection?

    private bool _recreateFile;

    public SessionProvider(IAppSettings coreAppSettings)
    {
      _coreAppSettings = coreAppSettings;
    }

    static IAppSettings _coreAppSettings;

    private static string SqlFileName
    {
      get
      {
        return _coreAppSettings.DbFilePath;
        
        //var settingsBase = IoC.Container.Resolve<ApplicationSettingsBase>();
        //return settingsBase.Context["DbFilePath"].ToString();
        //// return settingsBase["DbFilePath"].ToString();
      }
    }

    #region ISessionProvider Members

    public void PrepareSessionFactory()
    {
      PrepareSessionFactory(false);
    }

    public void PrepareSessionFactory(bool createDbFile)
    {
      if (_sessionFactory == null | createDbFile)
      {
        _sessionFactory = Configuration(createDbFile).BuildSessionFactory();
      }
    }

    public ISession GetFreshSession()
    {
      var factory = SessionFactory();

      ISession session;
      if (CurrentSessionContext.HasBind(factory))
      {
        session = CurrentSessionContext.Unbind(factory);
        session.Close();
        session.Clear();
      }

      session = factory.OpenSession();
      CurrentSessionContext.Bind(session);

      return session;
    }

    public ISession GetSession()
    {
      return GetSession(false);
    }

    public ISession GetSession(bool recreateFreshSession)
    {
      var factory = SessionFactory();
      ISession session;
      
      if(recreateFreshSession)
      {
        if (CurrentSessionContext.HasBind(factory))
        {
          session = CurrentSessionContext.Unbind(factory);
          session.Close();
        }
      }
      
      if(CurrentSessionContext.HasBind(factory))
      {
        return factory.GetCurrentSession();
      }
     
      session = factory.OpenSession();
      CurrentSessionContext.Bind(session);

      return session;
    }

    #endregion

    public FluentConfiguration Configuration(bool createDbFile)
    {
      _recreateFile = createDbFile || !File.Exists(_coreAppSettings.DbFilePath);
      if(_recreateFile)
      {
        _configuration = null;
      }

      if (_configuration == null | createDbFile)
      {
        _configuration = Fluently
              .Configure()
              .Database(SQLiteConfiguration.Standard.UsingFile(SqlFileName))
              .Mappings(m => m.FluentMappings.AddFromAssemblyOf<PersonMap>())
              .ExposeConfiguration(BuildSchema);
        //.Mappings(m => m.AutoMappings.Add(AutoPersistenceModel.MapEntitiesFromAssemblyOf<EntityBase>().Where(t => t.IsInstanceOfType(typeof(EntityBase)))))
      }
      return _configuration;
    }

    public ISessionFactory SessionFactory()
    {
      PrepareSessionFactory();
      return _sessionFactory;
    }

    private void BuildSchema(Configuration config)
    {
      if (_recreateFile)
      {
        var backupFileName = SqlFileName + ".bak";
        var tempFileName = SqlFileName + ".temp";

        File.Delete(tempFileName);

        // delete the existing db on each run  
        if (File.Exists(SqlFileName))
        {
          File.Move(SqlFileName, tempFileName);
          // if this fails, source may be in use or other...
          // exception will be thrown and handled elsewhere
        }

        File.Delete(backupFileName);

        if (File.Exists(tempFileName))
        {
          File.Move(tempFileName, backupFileName);
        }

        _recreateFile = false;

        // this NHibernate tool takes a configuration (with mapping info in)  
        // and exports a database schema from it  
        new SchemaExport(config)
              .Create(false, true);
      }

      config.SetProperty("current_session_context_class", "thread_static");

    }
  }
}